Skip to content

feat(idempotency): support for Redis as idempotency backend #3896

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 61 commits into from
May 12, 2025

Conversation

arnabrahman
Copy link
Contributor

@arnabrahman arnabrahman commented May 6, 2025

Summary

This Pr adds support for using Redis as an idempotency backend. The implementation follows the same convention as the Python implementation of the same feature for powertools

Changes

  • Add Redis support for idempotency backend
  • New layer class created for redis, RedisPersistanceLayer, this more or less follows the python implementation
  • Added some console logging similar to the Python implementation, but I think in this project, there is no precedent for doing that. So not sure if it should stay or not.
  • RedisConnection class to handle Redis client creation
  • Using@redis/client package as default redis client, but the user can bring their own client to use it.
  • Unit tests written for the code
  • This Pr only has the code & unit tests, documentation/integration tests would go inside a different PR

Example:

When using default redis client

import { randomUUID } from 'node:crypto';
import { makeIdempotent } from '@aws-lambda-powertools/idempotency';
import type { Context } from 'aws-lambda';
import type { Request, Response, SubscriptionResult } from './types.js';
import { RedisPersistenceLayer } from '@aws-lambda-powertools/idempotency/redis';

// Need to call the `init` method to connect to redis
const persistenceStore = await new RedisPersistenceLayer({
  url: 'redis://localhost:6379',
}).init();

const createSubscriptionPayment = async (
  event: Request
): Promise<SubscriptionResult> => {
  // ... create payment
  return {
    id: randomUUID(),
    productId: event.productId,
  };
};

export const handler = makeIdempotent(
  async (event: Request, _context: Context): Promise<Response> => {
    try {
      const payment = await createSubscriptionPayment(event);

      return {
        paymentId: payment.id,
        message: 'success',
        statusCode: 200,
      };
    } catch (error) {
      throw new Error('Error creating payment');
    }
  },
  {
    persistenceStore,
  }
);

When using your own redis client

import { createClient } from '@redis/client';
import { randomUUID } from 'node:crypto';
import { makeIdempotent } from '@aws-lambda-powertools/idempotency';
import type { Context } from 'aws-lambda';
import type { Request, Response, SubscriptionResult } from './types.js';
import { RedisPersistenceLayer } from '@aws-lambda-powertools/idempotency/redis';
import type { RedisClientProtocol } from '@aws-lambda-powertools/idempotency/redis/types';

const redisClient = createClient({ url: 'redis://localhost:6379' });
await redisClient.connect();

const persistenceStore = new RedisPersistenceLayer({
  client: redisClient as RedisClientProtocol,
});

const createSubscriptionPayment = async (
  event: Request
): Promise<SubscriptionResult> => {
  // ... create payment
  return {
    id: randomUUID(),
    productId: event.productId,
  };
};

export const handler = makeIdempotent(
  async (event: Request, _context: Context): Promise<Response> => {
    try {
      const payment = await createSubscriptionPayment(event);

      return {
        paymentId: payment.id,
        message: 'success',
        statusCode: 200,
      };
    } catch (error) {
      throw new Error('Error creating payment');
    }
  },
  {
    persistenceStore,
  }
);

Issue number: #3183


By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Disclaimer: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful.

@dreamorosi
Copy link
Contributor

Hi @arnabrahman, thank you!

Yes feel free to streamline wherever applicable, especially when it comes to types. Good idea!

@arnabrahman arnabrahman marked this pull request as ready for review May 7, 2025 15:07
@arnabrahman arnabrahman requested a review from dreamorosi May 7, 2025 15:07
Copy link
Contributor

@leandrodamascena leandrodamascena left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @arnabrahman and @dreamorosi, I have nothing to add here, the implementation is following all the standards we observed when doing this in Python. To be honest, it's a good amount of great work, congrats.

BTW, you'll open another PR to update the documentation @dreamorosi ?

@arnabrahman
Copy link
Contributor Author

@leandrodamascena Yes, we agreed to create separate Pr for documentation. If this pr is merged, i can start on the documentation part.

@dreamorosi dreamorosi dismissed their stale review May 12, 2025 09:15

Committed to the branch

@dreamorosi dreamorosi removed their request for review May 12, 2025 09:15
@dreamorosi
Copy link
Contributor

@leandrodamascena I need your review/approval again.

As discussed I have renamed the new persistence layer to CachePersistenceLayer to be more generic, I have also changed all mentions in types/comments/code to reflect this change. The logic hasn't changed.

In terms of how to describe this new persistence layer, I have copied the wording from this page, so from now on I think we'll address this as "Valkey-, and Redis OSS-compatible".

The API reference now looks like this, and shows both Glide and Redis clients:

image

Copy link
Contributor

@leandrodamascena leandrodamascena left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want, just fix the typo. Approving.

Co-authored-by: Leandro Damascena <lcdama@amazon.pt>
@dreamorosi dreamorosi merged commit 3352b90 into aws-powertools:main May 12, 2025
41 checks passed
@arnabrahman arnabrahman deleted the 3183-idempotency-redis branch May 12, 2025 11:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dependencies Changes that touch dependencies, e.g. Dependabot, etc. feature PRs that introduce new features or minor changes idempotency This item relates to the Idempotency Utility size/XL PRs between 500-999 LOC, often PRs that grown with feedback tests PRs that add or change tests
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Feature request: support for Redis in Idempotency
3 participants